using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.Profiling;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;

namespace Scene.Scripts
{
    public partial class SceneBox
    {
        public static Bounds FindBoxBounds(int boxId)
        {
            return SceneManager.FindBox(boxId).BoxBounds;
        }

        internal static void RemoveSceneObj(int boxId,AssetDetailType detailType, SceneObj obj)
        {
            var box = SceneManager.FindBox(boxId);
            if (box.sceneObjMap.TryGetValue(detailType, out var state))
            {
                state.Objs.Remove(obj);
            }
        }
        
        private static class SceneManager
        {
            public class DynamicObj
            {
                public Vector3 cachePosition;
                public DynamicObjData dynamicObj;
                public List<int> belongBoxes;

                public DynamicObj(DynamicObjData obj, int[] boxes)
                {
                    this.dynamicObj = obj;
                    this.cachePosition = obj.objBounds.center;
                    this.belongBoxes = new List<int>(boxes);
                }
            }
            public static LinkedListNode<int> FindTarget(AssetDetailType detailType,
                System.Predicate<int> predicate)
            {
                if (!ReadyLoaded.TryGetValue(detailType, out var linklist))
                {
                    return null;
                }

                var p = linklist.First;
                while (p != null)
                {
                    if (predicate(p.Value))
                        return p;
                    p = p.Next;
                }

                return default;
            }

            private static void OnSceneUpdate()
            {
                if (ReadyLoaded.Count != 0)
                {
                    foreach (var kvp in ReadyLoaded)
                    {
                        //var p = kvp.Value.First;
                        var list = kvp.Value;
                        while (list.Count != 0)
                        {
                            var p = list.First;
                            var box = FindBox(p.Value);
                            if (box == null)
                                throw new Exception($"On SceneUpdate error,boxId = {p.Value}");
                            var loading = box.sceneObjMap[kvp.Key];
                            {
                                foreach (var obj in loading.Objs)
                                {
                                    SceneObj.MarkReadyLoad(box, obj, kvp.Key);
                                }
                            }
                            list.RemoveFirst();
                            RuntimeData.ReleaseNode(p);
                        }
                    }
                }
            }

            private static Dictionary<SceneObj.IDynamicObj, DynamicObj> GameDynamicObjs;
            public static Dictionary<AssetDetailType, LinkedList<int>> ReadyLoaded;
            public static Dictionary<int, SceneBox> GameSceneBoxes;
            public static ScenePoint[] AllScenePoints;
            public static bool LoadChangeSemaphore { get; private set; } = false;

            public static void Init(List<ScenePoint> points,List<SceneBox> boxes)
            {
                AllScenePoints = new ScenePoint[points.Count];
                System.Array.Copy(points.ToArray(), SceneManager.AllScenePoints, points.Count);
                GameSceneBoxes = new Dictionary<int, SceneBox>(boxes.Count);
                GameDynamicObjs = new Dictionary<SceneObj.IDynamicObj, DynamicObj>(200);
                foreach (var box in boxes)
                {
                    var newBox = new SceneBox(box.BoxId, box.BoxBounds);
                    GameSceneBoxes.Add(box.BoxId, newBox);
                }

                SceneBox.CurrentMaxLoadTimes = 0;
                ReadyLoaded = new Dictionary<AssetDetailType, LinkedList<int>>();
            }

            public static void RefreshScene(Vector3 center, LoadRadius[] radius)
            {
                LoadChangeSemaphore = false;
                var change = RefreshDynamic();
                if (RefreshStaticCheck(center, radius,change))
                {
                    LoadChangeSemaphore = true;
                    OnSceneUpdate();
                }
            }
            public static void RefreshList(AssetDetailType detailType, LinkedListNode<int> ptr, bool upOrDown)
            {
                var p = upOrDown ? ptr.Previous : ptr.Next;
                if (p == null)
                    return;
                if (upOrDown == true)
                {
                    if (GetLoadCount(ptr.Value,detailType) <= GetLoadCount(p.Value,detailType))
                    {
                        return;
                    }

                    (p.Value, ptr.Value) = (ptr.Value, p.Value);
                    RefreshList(detailType, p, true);
                }
                else
                {
                    if (GetLoadCount(p.Value,detailType) <= GetLoadCount(ptr.Value,detailType))
                    {
                        return;
                    }

                    (p.Value, ptr.Value) = (ptr.Value, p.Value);
                    RefreshList(detailType, p, false);
                }
            }

            public static void AddDynamicObj(SceneObj.IDynamicObj obj)
            {
                //obj.GetObj
            }

            public static void RemoveDynamicObj(SceneObj.IDynamicObj obj)
            {
                
            }
            public static SceneBox FindBox(int boxId)
            {
                return GameSceneBoxes.GetValueOrDefault(boxId);
            }
            private static void MarkPointLoad(ScenePoint point, SceneBox.AssetDetailType detailType)
            {
                foreach (var item in point.BoxIds)
                {
                    var box = FindBox(item);
                    box.OnLoad(detailType);
                }
            }

            private static void MarkPointUnload(ScenePoint point, SceneBox.AssetDetailType detailType)
            {
                foreach (var item in point.BoxIds)
                {
                    var box = FindBox(item);
                    box.OnUnload(detailType);
                }
            }

            private static readonly HashSet<KeyValuePair<SceneBox.AssetDetailType, ScenePoint>> cacheSet =
                new HashSet<KeyValuePair<SceneBox.AssetDetailType, ScenePoint>>(100);

            private static readonly HashSet<KeyValuePair<SceneBox.AssetDetailType, ScenePoint>> loadedSet =
                new HashSet<KeyValuePair<SceneBox.AssetDetailType, ScenePoint>>(100);

            private static Vector3 _centerCache = Vector3.negativeInfinity;

            private static List<KeyValuePair<AssetDetailType, ScenePoint>> newAddList =
                new List<KeyValuePair<AssetDetailType, ScenePoint>>(300);
            private static List<KeyValuePair<AssetDetailType, ScenePoint>> newRemoveList =
                new List<KeyValuePair<AssetDetailType, ScenePoint>>(300);
            private static bool RefreshStaticCheck(Vector3 center, LoadRadius[] radius,bool forceCheck = false)
            {
                if (AllScenePoints == null || AllScenePoints.Length < 9)
                    throw new Exception("error");

                if (!forceCheck)
                {
                    if (Vector3.Distance(center, _centerCache) < 5f)
                    {
                        return false;
                    }
                }

                _centerCache = center;
                cacheSet.Clear();
                for (int i = 0; i < AllScenePoints.Length; i++)
                {
                    var point = AllScenePoints[i];
                    foreach (var r in radius)
                    {
                        var overlap = SceneUtility.TestAABBOverlap(new Bounds(center, Vector3.one * r.radius),
                            point.PointBound.center);
                        //if (Vector3.Distance(center, point.Point) > r.radius)
                        if(overlap == false)
                            continue;
                        cacheSet.Add(new KeyValuePair<SceneBox.AssetDetailType, ScenePoint>(r.detailType, point));
                    }
                }

                if (cacheSet.Count == 0)
                    return false;

                // var newAddList = cacheSet.Where(item => !loadedSet.Contains(item)).ToList();
                newAddList.Clear();
                foreach (var item in cacheSet)
                {
                    if (!loadedSet.Contains(item))
                    {
                        newAddList.Add(item);
                    }
                }
                if (newAddList.Count != 0)
                {
                    CurrentMaxLoadTimes = 0;
                    foreach (var item in newAddList)
                    {
                        loadedSet.Add(item);
                        MarkPointLoad(item.Value, item.Key);
                    }
                }
                //var newRemoveList = loadedSet.Where(item => !cacheSet.Contains(item)).ToList();
                newRemoveList.Clear();
                foreach (var item in loadedSet)
                {
                    if (!cacheSet.Contains(item))
                    {
                        newRemoveList.Add(item);
                    }
                }
                if (newRemoveList.Count != 0)
                {
                    foreach (var item in newRemoveList)
                    {
                        loadedSet.Remove(item);
                        MarkPointUnload(item.Value, item.Key);
                    }
                }

                return newAddList.Count != 0 || newRemoveList.Count != 0;
            }

            private static List<int> _tmpDynamicList = new List<int>(6);
            private static bool RefreshDynamic()
            {
                bool anyDynamicChange = false;
                foreach (var kvp in GameDynamicObjs)
                {
                    var curBounds = kvp.Value.dynamicObj.objBounds;
                    var dynamicObj = kvp.Key;
                    curBounds = new Bounds(dynamicObj.DynamicTransform.position, curBounds.size);
                    var curPosition = curBounds.center;
                    if (Vector3.Distance(kvp.Value.cachePosition,curPosition) < 50)
                    {
                        continue;
                    }
                
                    kvp.Value.cachePosition = curPosition;
                    _tmpDynamicList.Clear();
                    foreach (var box in GameSceneBoxes)
                    {
                        var overlap = SceneUtility.TestAABBOverlap(box.Value.BoxBounds, curBounds);
                        if (!overlap)
                            continue;
                        _tmpDynamicList.Add(box.Key);
                    }
                
                    if (_tmpDynamicList.SequenceEqual(kvp.Value.belongBoxes) == false)
                    {
                        anyDynamicChange = true;
                    }
                }

                return anyDynamicChange;
            }
        }
    }

    public static class SceneAssetPool
    {
        public static void LoadAssetAsync(string assetId,Action<Mesh,Material> callback)
        {
            try
            {
                var assetNms = assetId.Split('|');
                Mesh mesh = AssetDatabase.LoadAssetAtPath<Mesh>($"{assetNms[0]}.asset");
                Material mat = AssetDatabase.LoadAssetAtPath<Material>($"{assetNms[1]}.mat");
                SceneUtility.LogInfo($"{assetNms[0]}.asset");
                SceneUtility.LogInfo($"{assetNms[1]}.mat");
                callback(mesh, mat);
            }
            catch (Exception e)
            {
                Debug.LogError($"资源{assetId}加载异常,{e.Message}");
            }
        }

        public static void Unload(UnityEngine.Object obj)
        {
            
        }

        #if UNITY_EDITOR
        private static Dictionary<string, Mesh> _gameMeshes = new Dictionary<string, Mesh>();
        private static Dictionary<string, Material> _gameMats = new Dictionary<string, Material>();
        public static (string,string) CollectAsset(Mesh mesh, Material mat)
        {
            var toPath1 = $"Assets/TestScene/AutoSceneAssets/{mesh.name}";
            var toPath2 = $"Assets/TestScene/AutoSceneAssets/{mat.name}";
            _gameMeshes.TryAdd(mesh.name, mesh);
            _gameMats.TryAdd(mat.name, mat);
            return (toPath1, toPath2);
        }

        public static void MoveAssets()
        {
            return;
            var toPath = "Assets/TestScene/AutoSceneAssets";
            foreach (var kvp in _gameMeshes)
            {
                var newMesh = Object.Instantiate(kvp.Value);
                newMesh.name = kvp.Key;
                AssetDatabase.CreateAsset(newMesh, $"{toPath}/{newMesh.name}.asset");
            }

            foreach (var kvp in _gameMats)
            {
                var newMat = Object.Instantiate(kvp.Value);
                newMat.name = kvp.Key;
                AssetDatabase.CreateAsset(newMat, $"{toPath}/{newMat.name}.mat");
            }
        }
        #endif
    }
}